package cs6238.server;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
import java.util.UUID;

import cs6238.common.DelegateEntry;
import cs6238.common.FileInfo;

/**
 * This file holds all the meta data for all the users on the system, 
 * it can be written/read back from disk.  It must be thread-safe.
 * 
 * Querying is done through a DelegateGraph
 */
public class FileAndDelegateEntryTable {
	
	public static String FILE_ENTRY_TABLE = "FileEntryTable.csv";   //DO NOT CHANGE EXCEPT IN UNIT TESTS
	public static String DELEGATE_ENTRY_TABLE = "DelegateEntryTable.csv";   //DO NOT CHANGE EXCEPT IN UNIT TESTS
	
	private List<FileEntry> _fileEntries;
	private List<DelegateEntry> _delegateEntries;

	public FileAndDelegateEntryTable(boolean readFromDisk){
		_fileEntries = new ArrayList<FileEntry>();
		_delegateEntries = new ArrayList<DelegateEntry>();
		
		if(readFromDisk){
			readFromDisk();
		}
	}
	
	public synchronized void addFileEntry(FileEntry data){
		_fileEntries.add(data);
		saveToDisk();
	}
	
	public synchronized void addDelegateEntry(DelegateEntry data){
		if(!data.hasExpired()){
			_delegateEntries.add(data);
			saveToDisk();
		}
	}
	
	protected synchronized List<FileEntry> getAllFileEntries(){
		return _fileEntries;
	}
	
	protected synchronized List<DelegateEntry> getAllDelegateEntries(){
		removeExpiredDelegations();
		
		return _delegateEntries;
	}
	
	protected synchronized void removeExpiredDelegations(){
		List<DelegateEntry> validEntries = new ArrayList<DelegateEntry>();
		
		for(DelegateEntry entry : _delegateEntries){
			if(!entry.hasExpired()){
				validEntries.add(entry);
			}
		}
		
		_delegateEntries = validEntries;
	}
	
	public synchronized void saveToDisk(){
		removeExpiredDelegations();
		
		FileWriter outFile;
		try {
			outFile = new FileWriter(FILE_ENTRY_TABLE);
			PrintWriter out = new PrintWriter(outFile);
			
			out.write(FileEntry.toCsvColumns());
			out.write("\n");
			
			for(FileEntry filedata : _fileEntries){
				out.write(filedata.toCSV());
				out.write("\n");
			}
			
			out.close();
			outFile.close();
			
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		FileWriter outFile2;
		try {
			outFile2 = new FileWriter(DELEGATE_ENTRY_TABLE);
			PrintWriter out = new PrintWriter(outFile2);
			
			out.write(DelegateEntry.toCsvColumns());
			out.write("\n");
			
			for(DelegateEntry delegate : _delegateEntries){
				out.write(delegate.toCSV());
				out.write("\n");
			}
			
			out.close();
			outFile2.close();
			
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
	}
	
	private void readFromDisk(){
		if(new File(FILE_ENTRY_TABLE).exists()){
			try {
				Scanner scan = new Scanner(new File(FILE_ENTRY_TABLE));
				
				//Skip the CSV headers, those are just for debugging in excel
				scan.nextLine();
				while(scan.hasNextLine()){
					String line = scan.nextLine().trim();
					if(line.length() == 0){
						continue;
					}
					_fileEntries.add(new FileEntry(line));
				}
				
				scan.close();
				
			} catch (FileNotFoundException e) {
				System.err.println("Could not open FileEntry file");
				e.printStackTrace();
			}
		}
		
		if(new File(DELEGATE_ENTRY_TABLE).exists()){
			try {
				Scanner scan = new Scanner(new File(DELEGATE_ENTRY_TABLE));
				
				//Skip the CSV headers, those are just for debugging in excel
				scan.nextLine();
				while(scan.hasNextLine()){
					String line = scan.nextLine().trim();
					if(line.length() == 0){
						continue;
					}
					_delegateEntries.add(new DelegateEntry(line));
				}
				
				scan.close();
				
			} catch (FileNotFoundException e) {
				System.err.println("Could not open DelegateEntry file");
				e.printStackTrace();
			}
		}
		
		removeExpiredDelegations();
	}

	public synchronized List<FileInfo> getFilesAvailableToHost(String host) {
		removeExpiredDelegations();
		
		DelegateGraph graph = new DelegateGraph(_fileEntries, _delegateEntries);
		graph.validateDelegateEntries();
		return graph.getAllVisibleFiles(host);
	}
	
	public synchronized boolean isAllowedToGet(String host, UUID fileId){
		List<FileInfo> filesAvailable = getFilesAvailableToHost(host);
		for(FileInfo info : filesAvailable){
			if(info.getFileId().equals(fileId)){
				return true;
			}
		}
		
		return false;
		
	}

	
	public synchronized UUID getFileId(String encryptedHashStr) {
		for(FileEntry entry : _fileEntries){
			if(entry.getEncryptedFileHash().equals(encryptedHashStr)){
				return entry.getFileId();
			}
		}
		
		return null;
	}

	public synchronized FileEntry getFileEntry(UUID fileId) {
		for(FileEntry entry : _fileEntries){
			if(entry.getFileId().equals(fileId)){
				return entry;
			}
		}
		return null;
	}
	
}
